package server;

import java.awt.MultipleGradientPaint;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import javax.jdo.PersistenceManager;
import Gui.Player;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import javax.jdo.Query;
import webWof.State;
import webWof.StrippedGameDB;
import webWof.WebMessage;
import webWof.WebPlayer;

public class WebGamesManager implements Serializable{

	private static final long serialVersionUID = 8404734948518828261L;
	private static int NumOfPlayers = 2;
	private static WebGamesManager instance;
	private ArrayList<ServerGame> games;
	private ArrayList<WebPlayer> activeUsers;
	private HashMap<String,Integer> gameRequests;
	int gamesNum = 0;


	public static WebGamesManager getInstance(){
		if (instance == null){
			instance = new WebGamesManager();
		}
		return instance;
	}

	private WebGamesManager(){
		games = new ArrayList<ServerGame>();
		activeUsers = new ArrayList<WebPlayer>();
		gameRequests = new HashMap<String, Integer>();
	}

	public boolean addUser(WebPlayer player){
		for (WebPlayer p : activeUsers ){
			if ( p.name == player.name){
				return false;
			}
		}
		activeUsers.add(player);
		return true;
	}

	public ArrayList<WebPlayer> getActiveUsers(){
		ArrayList<WebPlayer> retval = new ArrayList<WebPlayer>();
		for (WebPlayer p : activeUsers ){
			if ( p.state == State.PENDING){
				retval.add(p);
			}
		}
		return retval;
	}

	public WebMessage ProccessPlayTurnMsg(WebMessage msg) {
		WebMessage retMsg = null;
		//check msg params
		ServerGame game  = findGame(msg.gameID);
		if (game == null) return retMsg;
		WebPlayer player = game.getPlayer(msg.PlayerName);
		if (player == null) return retMsg;

		//check if the phrase was guessed by another player
		if (msg.roundNum < game.getRoundNum()){
			retMsg = game.createRoundEndMsg(msg);
		}
		//else, send the player the phrase
		else{
			retMsg = game.createStartTurnMsg(msg);
		}

		return retMsg;
	}

	public String ProccessEndGameMsg(WebMessage msg) {
		ServerGame game  = findGame(msg.gameID);
		if (game == null) return "";
		WebPlayer player = game.getWinner();
		String retval = String.format("The winner is %s with the score %d", player.name,player.score);
		return retval;
	}

	private ServerGame findGame(int gameId){
		ServerGame retval = null;
		for(ServerGame game: games){
			if (game.getID() == gameId){
				retval = game;
				break;
			}
		}
		return retval;	
	}

	public  int  SetUpGame(String[] newPlayers) {
		WebPlayer[] players;
		int  retval = -1;
		if (newPlayers.length  == 2){
			players = new WebPlayer[2];
			for(int i = 0 ; i < 2 ; i++){
				players[i] = getPlayer(newPlayers[i]);
				if (players[i] == null || 
						players[i].state != State.PENDING)
					return -1; 
				else 
					players[i].state = State.PLAYING;
			}	
			ServerGame game = new ServerGame(2);
			game.addPlayer(players[0]);
			game.addPlayer(players[1]);
			games.add(game);
			retval =  game.getID();
			gameRequests.put(newPlayers[1], retval);	
		}
		return retval;
	}

	private  WebPlayer getPlayer(String string) {
		WebPlayer retval = null;
		for (WebPlayer p : activeUsers ){
			if ( p.name.equalsIgnoreCase(string)){
				retval = p;
				break;
			}
		}
		return retval;
	}

	public WebMessage getPendingMsg(WebMessage msg) {
		WebMessage retval = null;
		ServerGame game = findGame(msg.gameID);
		//game.getPlayer(msg.PlayerName);
		//check the type of msg
		switch (msg.type) {
		case endTurn:
			//end turn
			game.proccessEndTurnMsg(msg);
			if (game.IsEnded()){
				retval = game.createEndTurnMsg(msg);
				return retval;
			}
			game.lockPlayerAndRealeseNext(msg.PlayerName);	
			//check if the phrase was guessed by this player
			if (game.getPhrase().Guessed()){
				game.handleRoundEnded(msg);
			}
			retval = game.createEndTurnMsg(msg);
			break;

		case startTurn:
			//start turn
			if (game.IsPlayerTurn(msg.PlayerName) &&
					!game.IsPlayerPlaying(msg.PlayerName)){

				if (msg.roundNum < game.getRoundNum()){
					retval = game.createRoundEndMsg(msg);
				}
				else{
					retval = game.createStartTurnMsg(msg);
				}
				game.PlayerPlaying(msg.PlayerName);
			}
			break;

		case endGame:  //some problems here!!!!!
			//TODO add option to join a new game after this one ended
			getPlayer(msg.PlayerName).state = State.DEACTIVE;
			game.removePlayer();
			game.forceEndTheGame();

			endGameUpdatePlayer(msg.PlayerName, msg.opScore);

			if (game.canRemove()){
				endGameUpdateGame(game);
				games.remove(game);

			}
			break;

		case kickPlayer: 
			WebPlayer p=getPlayer(msg.PlayerName);
			if (p!=null)
			p.state = State.DEACTIVE;
			break;
			
		default:
			break;

		}		
		return retval;
	}

	
	public void endGameUpdatePlayer(String name, int score) {
		PlayerDB pDB;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {

			Key k = KeyFactory.createKey(PlayerDB.class.getSimpleName(), name);
			pDB = pm.getObjectById(PlayerDB.class, k);	
			//update score in DB
			pDB.setScore(pDB.getScore()+score);
			//remove from activeUsers list
			for (WebPlayer p1 : activeUsers ){
				if ( p1.name.equals(name)) {
					activeUsers.remove(p1);
					break;
				}
			}
		}
		catch(Exception e){
		}
		finally {
			pm.close();
		}
	}

	
	public void endGameUpdateGame(ServerGame g) {
		GameDB gDB = new GameDB(new Date(), g.getID());
		gDB.setWinner(g.getWinner().getName());
		gDB.setLoser(g.getLoser().getName());
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			pm.makePersistent(gDB);
		}
		catch(Exception e){
		}
		finally {
			pm.close();
		}
	}


	public int CheckIfLegal(WebPlayer player) {
		int result =-1;
		for (WebPlayer p1 : activeUsers ){
			if ( p1.name.equals(player.name)) {
				return 2;
			}
		}

		//check password in DB
		Key k;
		PlayerDB pDB;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Query q=pm.newQuery(PlayerDB.class);
			q.setFilter(" name == '"+player.getName()+"' ");
			List<PlayerDB> pList = (List<PlayerDB>) q.execute();
			for( int i = 0; i<pList.size(); i++ ) {
				pDB=pList.get(i);
				if (pDB.getName().equalsIgnoreCase(player.getName()) &&
						pDB.getPassword().equalsIgnoreCase(player.getPassword()))
				{
					player.state = State.PENDING;
					player.setScore(pDB.getScore());
					activeUsers.add(player);
					result = 1;
				}
			}
		}
		catch(Exception e){
		}
		finally {
			pm.close();
		}
		return result;
	}

	public int CheckAndInsert(WebPlayer newPlayer, boolean isGuest) {
		int result = 1;
		if (isGuest){
			newPlayer.state = State.PENDING;
			activeUsers.add(newPlayer);
		}
		else {  //make sure new player's name is unique 

			//add query to check new player's name is unique
			// insert new user to DB
			//add to activeUsers only on sigh in, not registration

			Key k;
			PlayerDB pDB;
			PersistenceManager pm = PMF.get().getPersistenceManager();
			try {				
				Query q=pm.newQuery(PlayerDB.class);
				q.setFilter(" name == '"+newPlayer.getName()+"' ");
				List<PlayerDB> pList = (List<PlayerDB>) q.execute();
				for( int i = 0; i<pList.size(); i++ ) {
					pDB = pList.get(i);
					//if new player password is confirmed add to activePlayers list
					if (pDB.getName().equalsIgnoreCase(newPlayer.getName())) {
						result = -1;
						return result;
					}
				}
				pDB = new PlayerDB(newPlayer.getName(),newPlayer.getMail(),newPlayer.getPassword(),newPlayer.getScore());
				pm.makePersistent(pDB);
			}
			catch(Exception e){
			}
			finally {
				pm.close();
			}
		}
		return result;
	}


	public int findGameReq(String name) {
		int retval = -1;
		if (!name.isEmpty()){
			if (gameRequests.containsKey(name)){
				retval = gameRequests.get(name);
				gameRequests.remove(name);
			}
		}
		return retval;
	}


	public ArrayList<WebPlayer> getHighScores() {
		PlayerDB pDB;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query q=pm.newQuery(PlayerDB.class);
		q.setOrdering(" score descending ");
		q.setRange(0,10);
		List<PlayerDB> results=(List<PlayerDB>) q.execute();
		ArrayList<WebPlayer> l=new ArrayList<WebPlayer>();
		for (int i=0;(i<10)&&(i<results.size());i++){
			
			WebPlayer temp=new WebPlayer(results.get(i).getName());
			temp.score=results.get(i).getScore();
			l.add(temp);
		}
		pm.close();
		return l;
	}

	public ArrayList<StrippedGameDB> getGameHistory(String usrname) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query q=pm.newQuery(GameDB.class);
		//q.setOrdering(" date descending ");
		q.setFilter(" winner == '"+usrname+"' ");
		List<GameDB> results=(List<GameDB>) q.execute();
		ArrayList<GameDB> l=new ArrayList<GameDB>(results);
		q.setFilter(" loser == '"+usrname+"' ");
		//q.setOrdering(" date descending ");
		results= (List<GameDB>) q.execute();;
		l.addAll(results);
		ArrayList<StrippedGameDB> retval=new ArrayList<StrippedGameDB>();
		for (GameDB g:l){
			retval.add(new StrippedGameDB(g.getWinner(), g.getLoser(), g.getDate()));
		}
		pm.close();
		return retval;
	}

	public Exception clearactiveusers() {
		Exception d=null;
		try{
			for (WebPlayer p:activeUsers)
		activeUsers.remove(p);}
		catch(Exception e){
			d=e;
		}
		return d;
	}


}
